package cc.shacocloud.mirage.bean.impl;

import cc.shacocloud.mirage.bean.BeanDescriptionHandler;
import cc.shacocloud.mirage.bean.QualifierHandler;
import cc.shacocloud.mirage.bean.ScopeHandler;
import cc.shacocloud.mirage.bean.bind.Autowired;
import cc.shacocloud.mirage.bean.bind.Component;
import cc.shacocloud.mirage.bean.meta.*;
import cc.shacocloud.mirage.utils.ResolvableType;
import cc.shacocloud.mirage.utils.annotation.AnnotatedElementUtils;
import cc.shacocloud.mirage.utils.reflection.ReflectUtil;
import jakarta.inject.Inject;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 基于注解实现的 {@link BeanDescriptionHandler}，它使用注解和反射从 bean 中提取元数据以查找依赖项。
 * <p>
 * 扫描 bean 以查找 {@link Inject} 注释，以检测依赖项注入的 {@link java.lang.reflect.Constructor}、{@link java.lang.reflect.Field}和 {@link java.lang.reflect.Method}
 * 然后扫描它们以查找 {@link jakarta.inject.Qualifier} 注释，以查找依赖项所需的限定符
 *
 * @author 思追(shaco)
 * @see cc.shacocloud.mirage.bean.bind.Component
 * @see Inject
 * @see AnnotationBeanKeyLoader
 */
public class AnnotationBeanDescriptionHandler extends AbstractBeanHandler implements BeanDescriptionHandler {
    
    public AnnotationBeanDescriptionHandler(@NotNull QualifierHandler qualifierHandler,
                                            @NotNull ScopeHandler scopeHandler) {
        super(qualifierHandler, scopeHandler);
    }
    
    @Override
    public BeanDescription getBeanDescription(@NotNull BeanKey beanKey) {
        Class<?> beanClass = beanKey.getBeanClass();
        Method[] allMethods = ReflectUtil.getMethods(beanClass);
        Member[] allMembers = ReflectUtil.getMembers(beanClass);
        
        BeanConstructor beanConstructor = createConstructorDescription(beanClass);
        List<BeanInjectionPoint> injectionPoints = findInjectionPoints(allMethods, allMembers);
        boolean singleton = scopeHandler.isSingleton(beanClass);
        
        Component component = beanKey.getAnnotation(COMPONENT_ANNOTATION);
        
        return new BeanDescriptionImpl(beanKey, beanConstructor, injectionPoints, singleton, Objects.isNull(component) || component.lazy());
    }
    
    /**
     * 创建构造函数描述对象
     */
    @NotNull
    protected BeanConstructor createConstructorDescription(@NotNull Class<?> beanClass) {
        Constructor<?> constructor = findConstructor(beanClass);
        List<BeanKey> dependencies = getExecutableParameters(constructor);
        return new BeanConstructorImpl(constructor, dependencies);
    }
    
    /**
     * 寻找构造函数
     */
    protected Constructor<?> findConstructor(@NotNull Class<?> beanClass) {
        Constructor<?>[] declaredConstructors = ReflectUtil.getConstructors(beanClass);
        
        // 如果只有一个构造函数则使用它
        if (declaredConstructors.length == 1 && ReflectUtil.isPublic(declaredConstructors[0])) {
            return declaredConstructors[0];
        }
        
        // 匹配满足条件的构造函数
        List<Constructor<?>> matches = new ArrayList<>();
        for (Constructor<?> constructor : declaredConstructors) {
            if (isConstructorNeedsInjection(constructor)) {
                matches.add(constructor);
            }
        }
        
        if (matches.size() > 1) {
            throw new UnsupportedOperationException(String.format("在类型 %s 中找到多个满足注入的构造函数：%s",
                    beanClass, matches));
        } else if (matches.size() == 0) {
            throw new UnsupportedOperationException(String.format("在类型 %s 中找不到满足注入的构造函数：%s",
                    beanClass, matches));
        }
        
        return matches.get(0);
    }
    
    /**
     * 寻找注入点
     *
     * @see #isFieldNeedsInjection
     * @see #isMethodNeedsInjection
     * @see ReflectUtil#isOverridden
     */
    protected List<BeanInjectionPoint> findInjectionPoints(@NotNull Method[] allMethods,
                                                           @NotNull Member @NotNull [] allMembers) {
        List<BeanInjectionPoint> injectionPoints = new ArrayList<>();
        
        for (Member member : allMembers) {
            // 字段
            if (member instanceof Field) {
                final Field field = (Field) member;
                
                if (isFieldNeedsInjection(field)) {
                    FieldInjectionPoint fieldInjectionPoint = createFieldInjectionPoint(field);
                    injectionPoints.add(fieldInjectionPoint);
                }
            }
            // 方法
            else if (member instanceof Method) {
                final Method method = (Method) member;
                if (isMethodNeedsInjection(method) && !ReflectUtil.isOverridden(method, allMethods)) {
                    MethodInjectionPoint methodData = createMethodInjectionPoint(method);
                    injectionPoints.add(methodData);
                }
            } else {
                throw new UnsupportedOperationException("不支持的成员类型： " + member);
            }
        }
        
        return injectionPoints;
    }
    
    /**
     * 创建 {@link MethodInjectionPoint}
     */
    protected MethodInjectionPoint createMethodInjectionPoint(@NotNull Method method) {
        List<BeanKey> dependencies = getExecutableParameters(method);
        return new MethodInjectionPoint(method, dependencies);
    }
    
    /**
     * 创建 {@link FieldInjectionPoint}
     */
    @Contract("_ -> new")
    protected @NotNull FieldInjectionPoint createFieldInjectionPoint(@NotNull Field field) {
        String qualifier = qualifierHandler.getQualifier(field);
        
        // 判断是否必须的，默认是必须的
        Autowired autowired = AnnotatedElementUtils.getAnnotation(field, Autowired.class);
        boolean required = Objects.isNull(autowired) || autowired.required();
        
        BeanKey beanKey = createBeanKey(ResolvableType.forField(field), qualifier, required);
        return new FieldInjectionPoint(field, beanKey);
    }
    
}
